// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt
// Project developers.  See the top-level LICENSE file for dates and other
// details.  No copyright assignment is required to contribute to VisIt.

// ************************************************************************* //
//                            avtGHOSTFileFormat.C                           //
// ************************************************************************* //

#include <avtGHOSTFileFormat.h>

#include <vtkFloatArray.h>
#include <vtkRectilinearGrid.h>
#include <avtDatabaseMetaData.h>
#include <InvalidVariableException.h>
#include <InvalidFilesException.h>
#include <avtParallel.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef _WIN32
#include <io.h>
#define FSEEK _fseeki64
#define CLOSE ::_close
#define OPEN  ::_open
#define READ  ::_read
typedef __int64 off64_t; 
#else
#include <unistd.h>
#define FSEEK fseek
#define O_BINARY 0
#define CLOSE ::close
#define OPEN  ::open
#define READ  ::read
#ifdef __APPLE__
#define off64_t off_t 
#endif
#endif

using namespace std;

// ****************************************************************************
//  Method: avtGHOSTFileFormat constructor
//
//  Programmer: pugmire -- generated by xml2avt
//  Creation:   Mon May 6 14:21:26 PST 2013
//
// ****************************************************************************

avtGHOSTFileFormat::avtGHOSTFileFormat(const char *filename)
    : avtSTMDFileFormat(&filename, 1)
{
    metaDataFile = filename;
    nBlocks = PAR_Size();
    initialized = false;

    for (int i = 0; i < 3; i++)
        meshSz[i] = 0;
    
    ReadMetaData();
}


// ****************************************************************************
//  Method: avtGHOSTFileFormat::PopulateDatabaseMetaData
//
//  Purpose:
//      This database meta-data object is like a table of contents for the
//      file.  By populating it, you are telling the rest of VisIt what
//      information it can request from you.
//
//  Programmer: pugmire -- generated by xml2avt
//  Creation:   Mon May 6 14:21:26 PST 2013
//
// ****************************************************************************

void
avtGHOSTFileFormat::PopulateDatabaseMetaData(avtDatabaseMetaData *md)
{
    ReadMetaData();
    
    AddMeshToMetaData(md, "mesh", AVT_RECTILINEAR_MESH, NULL, nBlocks, 0, 3, 3);
    for (size_t i = 0; i < vars.size(); i++)
        AddScalarVarToMetaData(md, vars[i].first.c_str(), "mesh", AVT_NODECENT);
}


// ****************************************************************************
//  Method: avtGHOSTFileFormat::GetMesh
//
//  Purpose:
//      Gets the mesh associated with this file.  The mesh is returned as a
//      derived type of vtkDataSet (ie vtkRectilinearGrid, vtkStructuredGrid,
//      vtkUnstructuredGrid, etc).
//
//  Arguments:
//      domain      The index of the domain.  If there are NDomains, this
//                  value is guaranteed to be between 0 and NDomains-1,
//                  regardless of block origin.
//      meshname    The name of the mesh of interest.  This can be ignored if
//                  there is only one mesh.
//
//  Programmer: pugmire -- generated by xml2avt
//  Creation:   Mon May 6 14:21:26 PST 2013
//
// ****************************************************************************

vtkDataSet *
avtGHOSTFileFormat::GetMesh(int domain, const char *meshname)
{
    vtkRectilinearGrid *rv = vtkRectilinearGrid::New();

    int r0[3], r1[3];
    GetRange(domain, r0, r1);
    
    vtkFloatArray *coords[3];
    int dims[3];
    for (int i = 0; i < 3; i++)
    {
        dims[i] = r1[i]-r0[i];
        coords[i] = vtkFloatArray::New();
        coords[i]->SetNumberOfTuples(dims[i]);

        for (int j = 0; j < dims[i]; j++)
            coords[i]->SetTuple1(j, r0[i]+j);
    }

    // Data are FORTRAN ordered, so swap the X,Z coords.
    int swapDims[3] = {dims[2], dims[1], dims[0]};
    rv->SetDimensions(swapDims);
    rv->SetXCoordinates(coords[2]);
    rv->SetYCoordinates(coords[1]);
    rv->SetZCoordinates(coords[0]);    

    for (int i = 0; i < 3; i++)
        coords[i]->Delete();
        
    return rv;   
}


// ****************************************************************************
//  Method: avtGHOSTFileFormat::GetVar
//
//  Purpose:
//      Gets a scalar variable associated with this file.  Although VTK has
//      support for many different types, the best bet is vtkFloatArray, since
//      that is supported everywhere through VisIt.
//
//  Arguments:
//      domain     The index of the domain.  If there are NDomains, this
//                 value is guaranteed to be between 0 and NDomains-1,
//                 regardless of block origin.
//      varname    The name of the variable requested.
//
//  Programmer: pugmire -- generated by xml2avt
//  Creation:   Mon May 6 14:21:26 PST 2013
//
// ****************************************************************************

vtkDataArray *
avtGHOSTFileFormat::GetVar(int domain, const char *varname)
{
    int r0[3], r1[3];
    GetRange(domain, r0, r1);
    
    int fd = -1;
    for (size_t i = 0; i < vars.size(); i++)
        if (vars[i].first == varname)
        {
            fd = OPEN(vars[i].second.c_str(), O_RDONLY|O_BINARY);
            break;
        }
    
    if (fd < 0)
    {
        EXCEPTION1(InvalidVariableException, varname);
    }

    off64_t nX = r1[0]-r0[0];
    off64_t nY = r1[1]-r0[1];
    off64_t nZ = r1[2]-r0[2];
    int nVals = nX*nY*nZ;

    //off64_t offset = (nX*nY*r0[2]*sizeof(float));

    vtkDataArray *arr = vtkFloatArray::New();
    arr->SetNumberOfTuples(nVals);
    
    float *out = (float *)arr->GetVoidPointer(0);
    size_t nRead = READ(fd, out, nVals*sizeof(float)); (void) nRead;

    CLOSE(fd);
    return arr;
}


//****************************************************************************
// Method:  avtGHOSTFileFormat::ReadMetaData
//
// Purpose:
//   Read meta data
//
// Programmer:  Dave Pugmire
// Creation:    May  8, 2013
//
// Modifications:
//
//****************************************************************************

void
avtGHOSTFileFormat::ReadMetaData()
{
    if (initialized)
        return;

    char line[1024], varNm[1024], varFile[1024];
    ifstream ifile(metaDataFile.c_str());
    
    if (!ifile.is_open())
    {
        EXCEPTION1(InvalidFilesException, metaDataFile);
    }
    
    while (! ifile.eof())
    {
        ifile.getline(line, 1024);
        if (line[0] == '#')
            continue;
        if (strncmp(line, "MESH", 4) == 0)
            sscanf(line, "MESH %d %d %d", &meshSz[0], &meshSz[1], &meshSz[2]);
        else if (strncmp(line, "VAR", 3) == 0)
        {
            sscanf(line, "VAR %s %s", varNm, varFile);
            vars.push_back(pair<string,string>(varNm, varFile));
        }
    }

    initialized = true;
}


//****************************************************************************
// Method:  avtGHOSTFileFormat::GetRange
//
// Purpose:
//   Get X,Y,Z extents for spatial domain.
//
// Programmer:  Dave Pugmire
// Creation:    May  8, 2013
//
// Modifications:
//
//****************************************************************************

void
avtGHOSTFileFormat::GetRange(int dom, int *r0, int *r1)
{
    r0[0] = r0[1] = r0[2] = 0;
    r1[0] = meshSz[0];
    r1[1] = meshSz[1];
    r1[2] = meshSz[2];

    if (nBlocks > 1)
    {
        int zPer = (r1[2]-r0[2])/nBlocks;
        r0[2] = dom*zPer;
        r1[2] = r0[2]+zPer+1;
        if (dom == nBlocks-1)
            r1[2] = meshSz[2];
    }
}
